package userlogic

import (
	"github.com/bytedance/sonic"
	"github.com/dgrijalva/jwt-go/v4"
	"github.com/fatih/structs"
	"github.com/gin-gonic/gin"
	"huijing_mall/internal/cachebase"
	"huijing_mall/internal/database/models"
	"huijing_mall/shared/common"
	"huijing_mall/shared/get_ctx_value"
	"huijing_mall/shared/kernel/ask"
	"huijing_mall/shared/kernel/hmErrors"
	"huijing_mall/variable"

	"huijing_mall/pkg/auth"
	"huijing_mall/pkg/logger"

	"net/http"
	"strconv"
	"strings"
	"time"
)

func LoginLogic(c *gin.Context, req ask.LoginRequest, mobile, email *string) {
	//获取 必要的值
	dbs := get_ctx_value.GetDBS(c)
	userCRUD := models.NewModelCRUD(models.UserType)

	switch req.LoginType {
	case "0": //手机号验证码 登录
		//校验 验证码
		code := cachebase.GetRedisCode(dbs.Rdb, common.StrPinJie(string(variable.TimeoutKey)+req.Link))
		if code != req.Sms {
			ask.Errors(c, http.StatusBadRequest, hmErrors.SmsErr)
			return
		}
		err := userCRUD.GetByField(dbs.DB, "email", req.Link)
		if err != nil && strings.Contains(err.Error(), "record not found") {
			err = userCRUD.GetByField(dbs.DB, "mobile", req.Link)
		}
		if err != nil {
			if !strings.Contains(err.Error(), "record not found") {
				ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("数据库查询失败", err.Error())))
				return
			}
		}
		if err != nil && strings.Contains(err.Error(), "record not found") { //第一次登录的 如果不存在 就去注册
			//密文 存储
			pwd, e := common.Encrypt(variable.PwdKeyNew, "Aa666888.") //默认密码 Aa666888.
			if e != nil {
				ask.Errors(c, 500, hmErrors.ErrSystem("密码加密失败"))
				return
			}
			userCRUD = &models.User{
				Username:     common.GetRandomString(11),
				Password:     pwd,
				Mobile:       mobile,
				AuthStatus:   variable.AuthUnverified,
				Integral:     0,
				Balance:      0,
				Avatar:       variable.DefaultAvatar,
				Email:        email,
				Nickname:     common.RandomUUIDStyleNickname(),
				RegisterDays: 1,
			}
			err = userCRUD.Create(dbs.DB)
			if err != nil {
				ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("注册失败", err.Error())))
				return
			}
			crud := typeToStrut(c, dbs, userCRUD)
			cookie, token, e := getUserMapCookieOrKeepToken(&dbs, *crud)
			if e != nil {
				ask.Errors(c, http.StatusInternalServerError, e)
				return
			}
			ginSetCookie(c, cookie)
			ask.Success(c, map[string]interface{}{"token": token})
			return
		}
		//类型断言
		crud := typeToStrut(c, dbs, userCRUD)
		cookie, token, e := getUserMapCookieOrKeepToken(&dbs, *crud)
		if e != nil {
			ask.Errors(c, http.StatusInternalServerError, e)
			return
		}
		ginSetCookie(c, cookie)
		ask.Success(c, map[string]interface{}{"token": token})
		return
	case "1":
		//拿下数据
		user, err := cachebase.RedisGet(dbs.Rdb, common.StrPinJie(req.Username, variable.UserDtaKey))
		if err == nil {
			_ = sonic.Unmarshal([]byte(user), userCRUD)
			logger.Info(user)
		} else {
			logger.Error(err.Error(), hmErrors.ErrOther("redis data nil go query mysql").Msg)

			err = userCRUD.GetByField(dbs.DB, "username", req.Username)
			if err != nil && strings.Contains(err.Error(), "record not found") {
				ask.Errors(c, http.StatusOK, hmErrors.UserNoFindErr)
				return
			}
			//类型断言
			crud, AErr := models.AssertModelCRUD[*models.User](userCRUD)
			if AErr != nil {
				ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("类型断言失败，无法修改商品数据", AErr.Error())))
				return
			}
			if !common.VerifyPasswordNew(req.Password, crud.Password, variable.PwdKeyNew) {
				pwdKey := common.StrPinJie(req.Username, "_", variable.PwdCount)
				if cachebase.RedisExist(dbs.Rdb, pwdKey) {
					//这里暂时 忽略错误
					if get, _ := cachebase.RedisGet(dbs.Rdb, pwdKey); get == "5" {
						ask.Errors(c, 200, hmErrors.PwdOutOfGaugeErr)
						return
					}
				} else {
					//压入队列 (只压第一次) 后面清除用
					err1 := cachebase.LPushList(dbs.Rdb, variable.PwdCount, pwdKey)
					if err1 != nil {
						ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("压入队列错误", err1.Error())))
						return
					}
				}
				cachebase.RedisIncr(dbs.Rdb, pwdKey)
				ask.Errors(c, 200, hmErrors.LoginPwdErr)
				return
			}
			err = crud.Update(dbs.DB)
			if err != nil {
				ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("更改失败", err.Error())))
				return
			}
			cookie, token, e := getUserMapCookieOrKeepToken(&dbs, *crud)
			if e != nil {
				ask.Errors(c, http.StatusInternalServerError, e)
				return
			}
			ginSetCookie(c, cookie)
			ask.Success(c, map[string]interface{}{"token": token})
			return
		}

		//类型断言
		crud := typeToStrut(c, dbs, userCRUD)
		cookie, token, e := getUserMapCookieOrKeepToken(&dbs, *crud)
		if e != nil {
			ask.Errors(c, http.StatusInternalServerError, e)
			return
		}
		ginSetCookie(c, cookie)
		ask.Success(c, map[string]interface{}{"token": token})
		return
	default:
		ask.Errors(c, http.StatusBadRequest, hmErrors.ErrMsg("参数错误!"))
		return
	}
}

func RegLogic(c *gin.Context, req ask.RegRequest, mobile, email *string) {

	dbs := get_ctx_value.GetDBS(c)
	userCRUD := models.NewModelCRUD(models.UserType)

	//校验 验证码
	code := cachebase.GetRedisCode(dbs.Rdb, common.StrPinJie(string(variable.TimeoutKey)+req.Link))
	if code != req.Sms {
		ask.NewErrors(c, http.StatusBadRequest, -1, hmErrors.SmsErr)
		return
	}
	err := userCRUD.GetByField(dbs.DB, "mobile", mobile)
	if err == nil {
		ask.NewErrors(c, http.StatusOK, -1, hmErrors.UserExistErr)
		return
	}
	//密文 存储
	pwd, e := common.Encrypt(variable.PwdKeyNew, "Aa666888.") //默认密码 Aa666888.
	if e != nil {
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.EncryptPwdErr)
		return
	}
	userCRUD = &models.User{
		Username:     common.RandomString(11),
		Password:     pwd,
		Mobile:       mobile,
		AuthStatus:   variable.AuthUnverified,
		Integral:     0,
		Balance:      0,
		Avatar:       variable.DefaultAvatar,
		Email:        email,
		Nickname:     req.Nickname,
		RegisterDays: 1,
	}
	err = userCRUD.Create(dbs.DB)
	if err != nil {
		logger.Error(err.Error())
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
		return
	}
	crud := typeToStrut(c, dbs, userCRUD)
	cookie, userMap, err := getUserMapCookieOrKeepToken(&dbs, *crud)
	if err != nil {
		logger.Error(err.Error())
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
		return
	}
	ginSetCookie(c, cookie)
	ask.Success(c, map[string]interface{}{"userdata": userMap})
	return
}

func UpPwdLogic(c *gin.Context, req ask.UpPwdRequest) {
	//userID := get_ctx_value.GetUID(c)
	userCRUD := models.NewModelCRUD(models.UserType)
	dbs := get_ctx_value.GetDBS(c)

	//校验 验证码
	code := cachebase.GetRedisCode(dbs.Rdb, common.StrPinJie(string(variable.TimeoutKey)+req.Link))
	if code != req.Sms {
		ask.NewErrors(c, http.StatusBadRequest, -1, hmErrors.SmsErr)
		return
	}
	sql := "SELECT * FROM user WHERE username = ? AND (mobile = ? OR email = ?)"
	err := userCRUD.FindByRawSQL(dbs.DB, sql, req.Username, req.Link, req.Link)
	if err != nil && strings.Contains(err.Error(), "record not found") {
		ask.NewErrors(c, 200, -1, hmErrors.UserNoFindErr)
		return
	}
	//密文 存储
	pwd, e := common.Encrypt(variable.PwdKeyNew, req.Password) //默认密码 Aa666888.
	if e != nil {
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.EncryptPwdErr)
		return
	}

	user, _ := models.AssertModelCRUD[*models.User](userCRUD)
	user.Password = pwd
	err = userCRUD.Update(dbs.DB)
	//err = database.UpdatePwd(dbs.DB, req.Username, req.Link, pwd)
	if err != nil {
		logger.Error(err.Error())
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
		return
	}

	ask.Success(c, nil)
}

func typeToStrut(c *gin.Context, dbs get_ctx_value.DBS, userCRUD models.ModelCRUD) (T *models.User) {
	//类型断言
	crud, AErr := models.AssertModelCRUD[*models.User](userCRUD)
	if AErr != nil {
		ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("类型断言失败，无法修改用户数据", AErr.Error())))
		return
	}
	now := time.Now()
	crud.LastLoginAt = &now
	err := crud.Update(dbs.DB)
	if err != nil {
		ask.Errors(c, 500, hmErrors.ErrSystem(common.StrPinJie("更改失败", err.Error())))
		return
	}
	return crud
}

// 生产并保活token token userMap
func getUserMapCookieOrKeepToken(dbs *get_ctx_value.DBS, u models.User) (cookie string, token string, err error) {
	//将用户信息 储存到redis 缓冲中 说不定某个地方需要获取用户数据
	err = cachebase.RedisSetUserData(dbs.Rdb, common.StrPinJie(strconv.Itoa(int(u.BaseModel.ID)), variable.UserDtaKey), u, variable.ExpiresIn)
	if err != nil {
		return "", "", hmErrors.ErrSystem(common.StrPinJie("存储redis失败", err.Error()))
	}
	//生成token
	token = auth.CreateToken(&auth.MyCustomClaims{
		UserId: u.BaseModel.ID,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: jwt.NewTime(float64(time.Now().Unix() + variable.ExpiresIn)),
		},
	})
	//保活一下
	err = cachebase.RdKeepToken(dbs.Rdb, common.StrPinJie(common.IntToStr(int(u.BaseModel.ID)), variable.TokenKey), token, variable.ExpiresIn)
	if err != nil {
		return "", "", hmErrors.ErrSystem(common.StrPinJie("token存储redis失败", err.Error()))
	}
	//生成cookie
	cookie = common.StrPinJie(common.RandomString(25), "-", token, "-", common.RandomString(25))
	// 转换为 map
	userMap := structs.Map(u)
	// 删除敏感字段
	common.MapDelFiled(userMap, []string{"Password", "UserBalance", "UserIntegral", "UserPhoto", "UserPhoto", "AuthStatus", "Model"})
	logger.Info(cookie)
	marshal, _ := sonic.Marshal(userMap)
	logger.Info(string(marshal))
	return cookie, token, nil
}

// 设置 cookie
func ginSetCookie(c *gin.Context, cookie string) {
	c.SetCookie(variable.CookieKey, cookie, variable.ExpiresIn, "/", "", false, false)
}

func LoginOut(dbs get_ctx_value.DBS, userID uint) {
	if !cachebase.RedisDel(dbs.Rdb, common.StrPinJie(common.IntToStr(int(userID)), variable.TokenKey)) {
		logger.Error("删除失败")
	}
	if !cachebase.RedisDel(dbs.Rdb, common.StrPinJie(common.IntToStr(int(userID)), variable.UserDtaKey)) {
		logger.Error("删除失败")
	}
	logger.Info("删除成功")
}
